﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Security;
using SPMS.Data;

namespace SPMS.Common
{
    /// <summary>
    /// 窗体认证辅助类
    /// </summary>
    public static class FormsAuthenticationHelper
    {
        #region ---Public Method

        /// <summary>
        /// 获得认证编号。
        /// </summary>
        /// <param name="request">Web请求对象。</param>
        /// <returns>
        /// 认证编号。
        /// </returns>
        public static string GetAuthenticationID(HttpRequestBase request)
        {
            //获得认证Cookie
            HttpCookie cookie = request.Cookies[FormsAuthentication.FormsCookieName];

            //返回认证编号
            return (null == cookie) ? null : string.Empty;
        }

        /// <summary>
        /// 获得当前系统会话的会话编号。
        /// </summary>
        /// <returns></returns>
        public static Guid GetSystemSessionID()
        {
            //获得当前用户信息
            FormsPrincipal principal = GetCurrentPrincipal();

            //返回会话编号
            return principal.SessionID;
        }

        /// <summary>
        /// 获得当前认证用户信息。
        /// </summary>
        /// <returns>当前认证用户信息。</returns>
        public static FormsPrincipal GetCurrentPrincipal()
        {
            //判断是否已经登录
            if (HttpContext.Current.Request.IsAuthenticated)
            {
                //判断认证对象是否存在
                if (null == HttpContext.Current.User)
                {
                    //抛出异常
                    throw new UnauthenticatedException();
                }

                //判断是否为窗体认证用户信息
                if (HttpContext.Current.User is FormsPrincipal)
                {
                    //返回当前认证用户信息
                    return (FormsPrincipal)HttpContext.Current.User;
                }

                //获得认证用户标识
                FormsIdentity identity = HttpContext.Current.User.Identity as FormsIdentity;

                //判断是否为窗体认证用户标识
                if (null == identity)
                {
                    //抛出异常
                    throw new BusinessException(SPMS.Common.Properties.Resources.text_error_authentication_mode_invalid);
                }

                //用于保存会话编号
                Guid sessionID = Guid.Empty;

                //获得会话编号
                if (!Guid.TryParse(identity.Ticket.UserData, out sessionID))
                {
                    //执行登出操作
                    FormsAuthentication.SignOut();

                    //如果无法取回会话编号，则认为没有登录
                    throw new UnauthenticatedException();
                }

                //用于保存用户编号
                Guid userID = Guid.Empty;

                //获得用户编号
                if (!Guid.TryParse(identity.Ticket.Name, out userID))
                {
                    //执行登出操作
                    FormsAuthentication.SignOut();

                    //如果无法取用户编号，则认为没有登录
                    throw new UnauthenticatedException();
                }

                //创建上下文
                using (AccountFileManagementEntities context = new AccountFileManagementEntities())
                {
                    //查询用户信息
                    User logonUser = context.Users.FirstOrDefault(item => item.User_ID == userID);

                    //判断用户信息是否存在
                    if (null == logonUser)
                    {
                        //执行登出操作
                        FormsAuthentication.SignOut();

                        //如果无法取用户信息，则认为没有登录
                        throw new UnauthenticatedException();
                    }

                    //查询会话信息
                    Session currentSession = context.Sessions.FirstOrDefault(item => item.Session_ID == sessionID);

                    //判断会话信息是否存在
                    if (null == currentSession)
                    {
                        //恢复会话
                        currentSession = new Session()
                        {
                            Session_ID = Guid.NewGuid(),
                            User_ID = userID,
                            CreateTime = DateTime.Now
                        };

                        //添加会话
                        context.Sessions.Add(currentSession);

                        //提交变化
                        context.SaveChanges();
                    }
                    else
                    {
                        //判断会话是否有效
                        if (currentSession.User_ID != userID)
                        {
                            //执行登出操作
                            FormsAuthentication.SignOut();

                            //如果无法取用户信息，则认为没有登录
                            throw new UnauthenticatedException();
                        }
                    }

                    //查询用户角色信息
                    var queryRole = from item in context.UserRoleViews
                                    where item.User_ID == userID
                                    select item;

                    //获得用户的角色列表
                    UserRoleView[] roles = queryRole.ToArray();

                    //查询用户拥有的功能列表
                    var queryFun = from item in context.UserFunctionalityViews
                                   where item.User_ID == userID
                                   select item.Functionality_Code;

                    //获得用户功能列表
                    int[] functionalities = queryFun.ToArray();

                    //创建用户信息
                    FormsPrincipal principal = new FormsPrincipal(identity, logonUser, sessionID, roles, functionalities);

                    //设置为当前用户信息
                    HttpContext.Current.User = principal;

                    //设置为当前用户
                    System.Threading.Thread.CurrentPrincipal = principal;

                    //返回用户信息
                    return principal;
                }
            }
            else
            {
                //抛出异常
                throw new UnauthenticatedException();
            }
        }

        /// <summary>
        /// 计算指定密码的哈希值。
        /// </summary>
        /// <param name="password">要计算哈希值的密码。</param>
        /// <returns>指定密码的哈希值。</returns>
        public static string HashPassword(string password)
        {
            //获得密码的哈希字节
            byte[] hashedPasswordBytes = GetHashedPasswordBytes(password);

            //返回哈希后的密码
            return Convert.ToBase64String(hashedPasswordBytes);
        }

        /// <summary>
        /// 判断输入的登录密码是否与哈希后的密码一致。
        /// </summary>
        /// <param name="inputPassword">输入的密码。</param>
        /// <param name="storedPassword">存储的密码。</param>
        /// <returns>
        ///   如果一致，则返回<c>true</c>，否则返回<c>false</c>。
        /// </returns>
        public static bool IsPasswordEqual(string inputPassword, string storedPassword)
        {
            return true;

            //获得存储密码的二进制值
            byte[] storedData = Convert.FromBase64String(storedPassword);

            //用于保存散列的密码
            byte[] hashedData = new byte[storedData.Length - PASSWORDSALTLENGTH];

            //用于保存盐
            var salt = new byte[PASSWORDSALTLENGTH];

            //从密码中获得盐
            Array.Copy(storedData, 0, salt, 0, PASSWORDSALTLENGTH);

            //获得散列的密码
            Array.Copy(storedData, PASSWORDSALTLENGTH, hashedData, 0, hashedData.Length);

            //获得输入密码的哈希值
            byte[] inputData = Encoding.UTF8.GetBytes(inputPassword);

            //用于保存密码
            var tempData = new byte[inputData.Length + PASSWORDSALTLENGTH];

            //复制数据
            Array.Copy(salt, 0, tempData, 0, salt.Length);
            Array.Copy(inputData, 0, tempData, PASSWORDSALTLENGTH, inputData.Length);

            //创建哈希算法
            using (SHA256 sha256 = new SHA256Managed())
            {
                //计算哈希值
                inputData = sha256.ComputeHash(tempData);
            }

            //返回比较结果
            return CompareBytes(inputData, hashedData);
        }

        #endregion

        #region ---Property

        /// <summary>
        /// 获得当前登录用户信息。
        /// </summary>
        /// <value>
        /// 当前登录用户信息。
        /// </value>
        public static User LogonUser
        {
            get
            {
                //获得当前用户信息
                FormsPrincipal principal = GetCurrentPrincipal();

                //返回用户信息
                return principal.LogonUser;
            }
        }

        #endregion

        #region ---Private Method

        /// <summary>
        /// 获得随机字节。
        /// </summary>
        /// <param name="size">随机字节的长度。</param>
        /// <returns>包含随机字节的字节数组。</returns>
        private static byte[] GetRandomBytes(int size)
        {
            //用于保存随机数
            var random = new Byte[size];

            //创建随机数提供者
            var provider = new RNGCryptoServiceProvider();

            //生成随机数
            provider.GetNonZeroBytes(random);

            //返回随机数
            return random;
        }

        /// <summary>
        /// 获得指定密码的哈希字节数组
        /// </summary>
        /// <param name="password">The password.</param>
        /// <returns></returns>
        private static byte[] GetHashedPasswordBytes(string password)
        {
            //生成盐
            var salt = GetRandomBytes(PASSWORDSALTLENGTH);

            //获得输入密码内容
            var enteredPassword = Encoding.UTF8.GetBytes(password);

            //用于保存密码内容
            var passwordByteList = new List<byte>();

            //添加盐
            passwordByteList.AddRange(salt);
            //添加密码
            passwordByteList.AddRange(enteredPassword);

            //用于保存哈希结果
            byte[] passwordHashResult;

            //创建哈希算法
            using (SHA256 sha256 = new SHA256Managed())
            {
                //计算哈希值
                passwordHashResult = sha256.ComputeHash(passwordByteList.ToArray());
            }

            //清空内容
            passwordByteList.Clear();
            //添加盐
            passwordByteList.AddRange(salt);
            //添加密码哈希值
            passwordByteList.AddRange(passwordHashResult);

            //返回结果
            return passwordByteList.ToArray();
        }

        /// <summary>
        /// 比较字节数组是否相等。
        /// </summary>
        /// <param name="left">用于比较的字节数组。</param>
        /// <param name="right">用于比较的字节数组。</param>
        /// <returns>如果相等，则返回<c>true</c>,否则返回<c>false</c>。</returns>
        private static bool CompareBytes(byte[] left, byte[] right)
        {
            //比较长度是否相等
            if (left.Length != right.Length)
            {
                return false;
            }

            //循环比较每一个字节
            for (int i = 0; i < left.Length; i++)
            {
                if (left[i] != right[i])
                {
                    return false;
                }
            }

            //默认返回相等
            return true;
        }

        #endregion

        #region ---Const

        /// <summary>
        /// 会话编号存储键值
        /// </summary>
        public const string KEY_SESSIONID = "_SessionID";

        /// <summary>
        /// 密码盐长度
        /// </summary>
        private const int PASSWORDSALTLENGTH = 16;

        #endregion
    }
}
